#include <windows.h>
#include <vector>
#include <string>
using namespace std;

LPCTSTR ClsName = "FALL11201";
LPCTSTR WndName = "My Piano for CSCI201";

LRESULT CALLBACK WndProcedure(HWND hWnd, UINT uMsg,
			      WPARAM wParam, LPARAM lParam);

int s1tw=14;

int s1mh=50;  // also a half note height

int s1bw=24;

int s1lh=80;

int s2w=13; // half note width
	

int s4tw=10;
const int keynum=88;
	
int keys[keynum];
int marks[keynum];
int currentx;
int currenty;


/*	
vector<int> chordpattern;
int root;

void markchords(int root)
{

	for(int i=0;i<keynum;i++)
		marks[i]=0;

	marks[root]=1;
	marks[root+3]=1;
	marks[root+7]=1;
}

void populate_chordpattern(char * chorddistances)
{

  char * pch;
  chordpattern.clear();

  pch = strtok (chorddistances,"|");
  while (pch != NULL)
  {

	  //printf ("%s\n",pch);
      chordpattern.push_back (atoi(pch));
	  pch = strtok (NULL, "|");
  }

  //chordpattern.push_back (4);
 

}
void setroot(int pr)
{
	root=pr;
}
*/

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow)
{
	MSG        Msg;
	HWND       hWnd;
	WNDCLASSEX WndClsEx;
	
	int width, height;

	width=1400, height=500;


	// Create the application window
	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc   = WndProcedure;
	WndClsEx.cbClsExtra    = 0;
	WndClsEx.cbWndExtra    = 0;
	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	WndClsEx.lpszMenuName  = NULL;
	WndClsEx.lpszClassName = ClsName;
	WndClsEx.hInstance     = hInstance;
	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

	// Register the application
	RegisterClassEx(&WndClsEx);

	// Create the window object
	hWnd = CreateWindow(ClsName,
			  WndName,
			  WS_OVERLAPPEDWINDOW,
			  0,  // start position x
			  0,  // start position y
			  width,
			  height,
			  NULL,
			  NULL,
			  hInstance,
			  NULL);

	
	
	// Find out if the window was created
	if( !hWnd ) // If the window was not created,
		return 0; // stop the application

	// Display the window to the user
	ShowWindow(hWnd, SW_SHOWNORMAL);
	UpdateWindow(hWnd);

	// Decode and treat the messages
	// as long as the application is running
	while( GetMessage(&Msg, NULL, 0, 0) )
	{
             TranslateMessage(&Msg);
             DispatchMessage(&Msg);
	}

	return Msg.wParam;
}

	


void drawpolygon(HDC hDC, int type, int x, int y, int highlight)
{


	POINT *Pt1;
	HBRUSH      NewBrush;
	HGDIOBJ original = NULL;        
        
	int num;

	/*
	Pt1[0].x =  50;
	Pt1[0].y =  50;
	Pt1[1].x = 60;
	Pt1[1].y =  50;
	Pt1[2].x = 60;
	Pt1[2].y =  150;
	Pt1[3].x = 80;
	Pt1[3].y =  150;
	Pt1[4].x = 80;
	Pt1[4].y = 250;
	Pt1[5].x = 50;
	Pt1[5].y =  250;

	*/


	
	
	switch (type)
	{
		case 1:
			{
			
				num=6;
	Pt1=new POINT[num];
	// shape 1 top width
	Pt1[0].x =  x;
	Pt1[0].y =  y;
	Pt1[1].x = x+s1tw;
	Pt1[1].y =  y;
	Pt1[2].x = x+s1tw;
	Pt1[2].y =  y+s1mh;
	Pt1[3].x = x+s1bw;
	Pt1[3].y =  y+s1mh;
	Pt1[4].x = x+s1bw;
	Pt1[4].y = y+s1lh;
	Pt1[5].x = x;
	Pt1[5].y = y+s1lh;//shape 1 left hight

			
			}
			break;
		case 2:

			num=4;
			Pt1=new POINT[num];
			// shape 1 top width
			Pt1[0].x =  x;
			Pt1[0].y =  y;
			
			Pt1[1].x = x+s2w;
			Pt1[1].y =  y;

			Pt1[2].x = x+s2w;
			Pt1[2].y =  y+s1mh;

			Pt1[3].x = x;
			Pt1[3].y =  y+s1mh;

			//Polygon(hDC, Pt1, 7);
			break;
		case 3:
			num=6;
			Pt1=new POINT[num];
			// shape 1 top width
			Pt1[0].x =  x;
			Pt1[0].y =  y;
	
			Pt1[1].x = x+s1tw;
			Pt1[1].y =  y;
	
			Pt1[2].x = x+s1tw;
			Pt1[2].y =  y+s1lh;
	
			Pt1[3].x = x-(s1bw-s1tw)+3;
			Pt1[3].y =  y+s1lh;
	
			Pt1[4].x = x-(s1bw-s1tw)+3;
			Pt1[4].y = y+s1mh;
			
			Pt1[5].x = x;
			Pt1[5].y = y+s1mh;//shape 1 left hight

			break;
		case 4:
			num=8;
			Pt1=new POINT[num];
			// shape 1 top width
			Pt1[0].x =  x;
			Pt1[0].y =  y;
	
			Pt1[1].x = x+s4tw;
			Pt1[1].y =  y;
	
			Pt1[2].x = x+s4tw;
			Pt1[2].y =  y+s1mh;
	
			Pt1[3].x = x+s4tw+(s1bw-s4tw)/2;
			Pt1[3].y =  y+s1mh;
	
			Pt1[4].x = x+s4tw+(s1bw-s4tw)/2;
			Pt1[4].y = y+s1lh;
			
			Pt1[5].x = x+s4tw+(s1bw-s4tw)/2-s1bw;
			Pt1[5].y = y+s1lh;//shape 1 left hight

			Pt1[6].x = x+s4tw+(s1bw-s4tw)/2-s1bw;
			Pt1[6].y = y+s1mh;//shape 1 left hight

			Pt1[7].x = x;
			Pt1[7].y = y+s1mh;//shape 1 left hight

			break;
		case 5:

			num=4;
			Pt1=new POINT[num];
			// shape 1 top width
			Pt1[0].x =  x;
			Pt1[0].y =  y;
			
			Pt1[1].x = x+s1bw;
			Pt1[1].y =  y;

			Pt1[2].x = x+s1bw;
			Pt1[2].y =  y+s1lh;

			Pt1[3].x = x;
			Pt1[3].y =  y+s1lh;

		
			break;

	}

	if (highlight==1)
		NewBrush = CreateSolidBrush(RGB(255, 0, 0));
	else 
	{
		if (type!=2)
			NewBrush = CreateSolidBrush(RGB(255, 0, 255));
		else
			NewBrush = CreateSolidBrush(RGB(255, 255, 0));
       
        
	}
		//    Saving the original object, say a brush or a pen etc.

		original =SelectObject(hDC, NewBrush);
        Polygon(hDC, Pt1, num);

		DeleteObject(NewBrush);

		SelectObject(hDC, original);

	delete Pt1;
}


int inoneoctave(int i)
{
	return (i+9)%12;// offset 9 because starting from A
}
void initialize()
{
//c, c#, d, d#, e, f, f#, g, g#, a, a#, b
	int type;
	
	for(int i=0;i<keynum;i++)
	{
		//keys[i]=
		//keys[i]= (i%2==0)?1:2;
		//keys[i]= i%4+1;
		int pos=inoneoctave(i);
		switch (pos)   
		{
		
		case 0:
			if (i==87)
				type=5;
			else
				type=1;
			break;
		case 5:
			type=1;
			break;
		case 1:
		case 3:
		case 6:
		case 8:
		case 10:
			type=2;
			break;
		case 2:
		case 7:
			type=4;
			break;
		case 9:
			if (i==0)
				type=1; //special case on the leftmost key which A, but takes a shape1 not shape4
			else
				type=4;
			break;
		case 4:
		case 11:
			type =3;
			break;
		}
		keys[i]=type;

		}

	
}

void markkey(int pos)
{
	for(int i=0;i<keynum;i++)
		marks[i]=0;

	marks[pos]=1;

}

void drawlabel(HDC hDC, char * label, int x, int y)
{
	HGDIOBJ originalfont;        
    
	HFONT displayfont = CreateFont(12, 0, 0, 0, FW_DONTCARE, false, false, false, OEM_CHARSET,
				OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
				DEFAULT_PITCH | FF_DONTCARE, "Arial");
	originalfont=SelectObject (hDC, displayfont);
	SetBkMode (hDC, TRANSPARENT);
	TextOut(hDC, x, y, label,strlen(label));   
	SelectObject(hDC,originalfont);
	SetBkMode(hDC,	OPAQUE); // the default mode, overriding
}


string noteslist[]={"C","C#","D","D#","E","F","F#","G","G#","A","A#","B"};

int getkeywidth(int type)
{
	int keywidth;
	switch (type)
	{
		case 1:
		case 3:
			keywidth=s1tw;
			break;
		case 2:
		
			keywidth=s2w;
			break;
		case 4:
			keywidth=s4tw;
			break;
		case 5:
			keywidth=s1bw;
			break;
	}

	return ++keywidth;
	// adding one pixel so the right border of the left key won't overlap with the left border of the right key.
	
}

int leftmargin=10;
int topmargin=150;

void drawpiano(HDC hDC)
{
	currentx=leftmargin;
	currenty=topmargin;

	for(int i=0;i<keynum;i++)
	{
		string label=noteslist[inoneoctave(i)];
		
		
		drawpolygon(hDC, keys[i], currentx,currenty,marks[i]);
		drawlabel(hDC, (char *)label.c_str(), currentx, currenty);
		currentx+=getkeywidth(keys[i]);
		
		
		/*
		if (keys[i]==1||keys[i]==3)
			{

				drawpolygon(hDC, keys[i], currentx,currenty,marks[i]);
				drawlabel(hDC, (char *)label.c_str(), currentx, currenty);

				currentx+=s1tw;
				currentx++;
			}
		else if (keys[i]==2)
			{
				drawpolygon(hDC, 2, currentx,currenty,marks[i]);
				drawlabel(hDC, (char *)label.c_str(), currentx, currenty);
				currentx+=s2w;
				currentx++;
			}
		else if (keys[i]==4)
		{
			drawpolygon(hDC, 4, currentx,currenty,marks[i]);
			drawlabel(hDC, (char *)label.c_str(), currentx, currenty);
			currentx+=s4tw;
			currentx++;
		}
		*/
	}

	

}



void drawkeyboard(HWND hWnd)
{
	HDC hDC;
	PAINTSTRUCT Ps;
	hDC = BeginPaint(hWnd, &Ps);

	drawlabel(hDC, "csci216", 100,100);  // call this function to draw string
	drawlabel(hDC, "csci216", 200,200);

	drawpiano(hDC);

	drawpolygon(hDC, 1, 50,50,0);

	drawpolygon(hDC, 2, 50+s1tw,50,1);
	
	drawpolygon(hDC, 4, 100,100,0);
	
	drawlabel(hDC, "csci216", 200,200);

    EndPaint(hWnd, &Ps);
     
}
int keypressed;

int maptokey(LPARAM lParam)
{
	int x,y;
	x=(short)LOWORD(lParam);
    y=(short)HIWORD(lParam);

	int cx;
	if (x<leftmargin||x>currentx||y<topmargin||y>topmargin+s1lh)
		return -1;
	else
	{
		// it is on the piano, but which key?
		//scan the key, accumlate the topwidth
		cx=leftmargin;
		
		int i;
		for(i=0;i<keynum;i++)
		{
			cx+=getkeywidth(keys[i]);
			if(cx>x)
				break;
		}

		if (keys[i]!=2 ||y<topmargin+s1mh)
			return i;
		else
		{
			//the position is right below a half note black key, but there are two possibilities.
			//need to figure out right or left, roughly half
			if ((cx-x)>s2w/2)
				return i+1;
			else
				return i-1;
		}
	}
	 
}


LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg,
			   WPARAM wParam, LPARAM lParam)
{
    
	switch(Msg)
    {
	case WM_CREATE:
		initialize();
		//markchords(28);
		break;
	case WM_ACTIVATE:
		
		break;

	case WM_PAINT:
	
		
		drawkeyboard(hWnd);
        break;

	case WM_LBUTTONDOWN:  
	case WM_RBUTTONDOWN: //whenever the right button of the mouse is pressed down.
		keypressed=maptokey(lParam);
		markkey(keypressed);

		Beep (440,1000);
		//http://msdn.microsoft.com/en-us/library/windows/desktop/ms679277(v=vs.85).aspx
		
		drawkeyboard(hWnd);
		// you can tell Windows that your window is dirty and needs a redraw by using this function, which will make the system to generate a WM_PAINT message
		InvalidateRect(hWnd,NULL,TRUE);
		break;

	case WM_SIZE:
		

		break;
	case WM_DESTROY:
        // then close it
        PostQuitMessage(WM_QUIT);
        break;
    default:
        // Process the left-over messages
        return DefWindowProc(hWnd, Msg, wParam, lParam);
    }
    // If something was not done, let it go
    return 0;
}
